
package com.joysuch.wwyt.edu.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.joysuch.wwyt.bp.bean.safetyworkcert.BpWorkApplyDetailDto;
import com.joysuch.wwyt.bp.bean.safetyworkcert.BpWorkApplyDto;
import com.joysuch.wwyt.bp.entity.BpDepart;
import com.joysuch.wwyt.bp.enums.DocTypes;
import com.joysuch.wwyt.bp.repository.BpDepartDao;
import com.joysuch.wwyt.bp.repository.BpJobDao;
import com.joysuch.wwyt.common.entity.CommonType;
import com.joysuch.wwyt.common.repository.CommonTypeDao;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.bean.BaseUserFindAllBean;
import com.joysuch.wwyt.core.bean.BpUserSearchBean;
import com.joysuch.wwyt.core.bean.ResultDto;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.DateTimeUtil;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.common.util.SimpleDateFormatCache;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.entity.dsl.QBaseUser;
import com.joysuch.wwyt.core.mapper.BaseUserMapper;
import com.joysuch.wwyt.core.repository.BaseTypeGroupDao;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.service.BaseUserService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.edu.bean.*;
import com.joysuch.wwyt.edu.bean.EduExamDto;
import com.joysuch.wwyt.edu.entity.*;
import com.joysuch.wwyt.edu.entity.dsl.QEduExam;
import com.joysuch.wwyt.edu.entity.dsl.QEduExamRecord;
import com.joysuch.wwyt.edu.enums.*;
import com.joysuch.wwyt.edu.mapper.*;
import com.joysuch.wwyt.edu.repository.*;
import com.joysuch.wwyt.edu.service.EduCourseService;
import com.joysuch.wwyt.edu.service.EduCourseStatisticsService;
import com.joysuch.wwyt.util.DateUtils;
import com.joysuch.wwyt.util.PageableUtil;
import com.joysuch.wwyt.util.PoiUtil;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.dsl.ComparableExpressionBase;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.shiro.util.Assert;
import org.redisson.api.RList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.persistence.Tuple;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author ZhangDong
 * @Date 2020/6/24 17:38
 */
@Service
@Slf4j
public class EduCourseStatisticsServiceImpl implements EduCourseStatisticsService {

    @Autowired
    private EduCourseStatisticsDao eduCourseStatisticsDao;
    @Autowired
    private EduCourseCoursewareDao eduCourseCoursewareDao;
    @Autowired
    private EduCourseService eduCourseService;
    @Autowired
    private BaseTypeGroupDao typeGroupDao;
    @Autowired
    private EduExamRecordDao eduExamRecordDao;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private BaseUserService baseUserService;
    @Autowired
    private BpDepartDao bpDepartDao;
    @Autowired
    private BpJobDao bpJobDao;
    @Autowired
    private EduQuestionDao eduQuestionDao;
    @Autowired
    private EduExamUserScopeDao eduExamUserScopeDao;
    @Autowired
    private EduExamDao eduExamDao;
    @Autowired
    private EduExamUserAnswerDao eduExamUserAnswerDao;
    @Autowired
    private EduCourseLearnHistoryDao historyDao;
    @Autowired
    private EduCoursewareDao coursewareDao;
    @Autowired
    private CommonTypeDao commonTypeDao;
    @Autowired
    private EduExamTestPaperDao examTestPaperDao;
    @Autowired
    private EduTestPaperDao testPaperDao;

    @Autowired
    private JPAQueryFactory jpaQueryFactory;

    @Autowired
    private EduTrainningRecordUserDao eduTrainningRecordUserDao;
    @Autowired
    private EduCourtTrainningRecordMapper eduCourtTrainningRecordMapper;

    @Autowired
    private EduCoursewareDao eduCoursewareDao;

    @Autowired
    private EduCourseLearnHistoryDao eduCourseLearnHistoryDao;

    @Autowired
    private EduExamRecordsMapper eduExamRecordsMapper;
    @Autowired
    private RedisClient redisClient;

    @Autowired
    private EduCoursewareMapper eduCoursewareMapper;

    @Autowired
    private EduTrainningRecordUserMapper eduTrainningRecordUserMapper;
    @Autowired
    private EduTrainningRecordMapper eduTrainningRecordMapper;

    @Autowired
    private EduCourseLearnHistoryMapper eduCourseLearnHistoryMapper;

    @Autowired
    private BaseUserMapper baseUserMapper;
    private static final String WWYT_LEARN_DOCUMENT = "wwyt_learn_document";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean learnTime(Long courseawareId, Integer learnTime, Integer lastTime, Integer state, Long trainingId) {
        Assert.isTrue(learnTime != null && learnTime > 0, "学习时间不符合要求");
        Long currentUserId = Context.getCurrentUserId();
//        //添加文档是否学完
//        EduCourseware courseware = eduCoursewareDao.getOne(courseawareId);
//        if (DocTypes.DOC.getCode().equals(courseware.getDocType())) {
//            int finish = eduCoursewareMapper.selectCowrseFinish(courseawareId, currentUserId, learnTime);
//            if (finish == 1) state = 1;
//        }
        EduCourseLearnHistory learnHistory = historyDao.findByCourseawareIdAndUserId(courseawareId, currentUserId);
        // 生成学习记录
        EduCourseLearnHistory history = new EduCourseLearnHistory();
        history.setUserId(currentUserId);
        history.setCourseawareId(courseawareId);
//        if (learnHistory != null && 0 == learnHistory.getState()) {
//            learnTime = learnHistory.getAppLearnTime() + learnTime;
//        }
        if (lastTime != null) {
            history.setLastTime(lastTime);
        }
        history.setState(state);
        //已播放完时长设置为0
//        if (1 == learnHistory.getState()) {
//            learnTime = 0;
//        }
        if (!ObjectUtils.isEmpty(trainingId)) {
            history.setTrainningRecordId(trainingId);
        }
        history.setAppLearnTime(learnTime);
        history.setCreateTime(LocalDateTime.now());
        history.setUpdateTime(LocalDateTime.now());
        historyDao.save(history);
        EduCourseStatistics eduCourseStatistics = eduCourseStatisticsDao.selectByUserId(currentUserId);
        if (eduCourseStatistics == null) {
            eduCourseStatistics = new EduCourseStatistics();
            eduCourseStatistics.setCourseLearnTime(Long.valueOf(learnTime));
            eduCourseStatistics.setUserId(Context.getCurrentUserId());
            List<Long> longs = new ArrayList<>();
            longs.add(courseawareId);
            eduCourseStatistics.setCoursewareLearned(JSON.toJSONString(longs));
            eduCourseStatistics.setCourseLearnTime(learnTime.longValue());
        } else {
            String coursewareLearned = eduCourseStatistics.getCoursewareLearned();
            List<Long> longs = JSON.parseArray(coursewareLearned, Long.class);
            longs = longs == null ? Lists.newArrayList() : longs;
            if (!longs.contains(courseawareId)) {
                longs.add(courseawareId);
                eduCourseStatistics.setCoursewareLearned(JSON.toJSONString(longs));
            }
            // 实行累加
            if (learnHistory == null || (learnHistory != null && learnHistory.getState() == 0)) {
                Long courseLearnTime = eduCourseStatistics.getCourseLearnTime();
                eduCourseStatistics.setCourseLearnTime(courseLearnTime + learnTime);
            }
        }
        eduCourseStatisticsDao.save(eduCourseStatistics);

        EduCourseLearnHistory now = historyDao.findByCourseawareIdAndUserId(courseawareId, currentUserId);
        if (now.getState() == 1 && !ObjectUtils.isEmpty(trainingId)) {
            EduTrainningRecord eduTrainningRecord = eduTrainningRecordMapper.selectByIdUnFinished(trainingId);
            if (!ObjectUtils.isEmpty(eduTrainningRecord)) {
                List<Long> userIds = new ArrayList<>();
                userIds.add(currentUserId);
                //培训合格标准为学完课程
                if (TrainingQualifiedStandard.FINISH_COURSE.getId() == eduTrainningRecord.getTrainingQualifiedStandard()) {
                    //用户是否学完课程
                    if (isFinishCourse(currentUserId, eduTrainningRecord.getId())) {
                        eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "Y", userIds);
                    }
//                        else {
//                            eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "N", userIds);
//                        }
                } else if (TrainingQualifiedStandard.FINISH_COURSE_EXAM.getId() == eduTrainningRecord.getTrainingQualifiedStandard()) {
                    //用户是否学完课程并且考试是否通过
                    if (isFinishCourse(currentUserId, eduTrainningRecord.getId())
                            && eduTrainningRecordUserMapper.selectPassExamByUserId(currentUserId, eduTrainningRecord.getExamId()) > 0) {
                        eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "Y", userIds);
                    }
                }
//                    else if (TrainingQualifiedStandard.FINISH_COURSE_EXAM.getId() == eduTrainningRecord.getTrainingQualifiedStandard()
//                            || TrainingQualifiedStandard.PASS_EXAM.getId() == eduTrainningRecord.getTrainingQualifiedStandard()) {
//                        if (!ObjectUtils.isEmpty(eduTrainningRecord.getExamId())) {
//                            //学完课程+考试通过
//                            if (isFinishCourse(currentUserId, trainingId)
//                                    && eduTrainningRecordUserMapper.selectExamByUserId(currentUserId, eduTrainningRecord.getExamId()) > 0
//                                    && eduTrainningRecordUserMapper.selectPassExamByUserId(currentUserId, eduTrainningRecord.getExamId()) > 0) {
//                                eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "Y", userIds);
//                            } else {
//                                eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "N", userIds);
//                            }
//                        }
//                    }
            } else {
                now.setState(0);
                eduCourseLearnHistoryMapper.updateById(now);
            }
        }
        return ResultBean.defaultSuccessResult();
    }

    private Boolean isFinishCourse(Long userId, Long trainingId) {
        List<Long> trainingCoursewares = eduTrainningRecordMapper.selectCoursewareByTrainingId(trainingId);
        List<Long> learns = eduTrainningRecordUserMapper.selectUserLearnCourseware(userId, trainingCoursewares, 1, trainingId);
        //是否学完课程
        return new HashSet<>(learns).containsAll(trainingCoursewares);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addExamCount(EduDoExamResultBean resultBean, Long examId, Long currentUserId, boolean isExamCount, boolean passed, boolean isFirstExam) {
        EduCourseStatistics eduCourseStatistics = eduCourseStatisticsDao.selectByUserId(currentUserId);
        if (eduCourseStatistics == null) {
            eduCourseStatistics = new EduCourseStatistics();
            eduCourseStatistics.setTenentId(Context.getCurrentTenantId() != null ? Context.getCurrentTenantId() : resultBean.getTenentId());
            eduCourseStatistics.setOrgCode(Context.getCurrentOrgCode() != null ? Context.getCurrentOrgCode() : resultBean.getOrgCode());
        }
        eduCourseStatistics.setUserId(currentUserId);
        eduCourseStatistics.setUpdateBy(currentUserId);
        Integer examCount = eduCourseStatistics.getExamCount();
        Integer examPassCount = eduCourseStatistics.getExamPassCount();
        //是第一次考试则正常增加考试数量和通过次数
        if (isFirstExam) {
            eduCourseStatistics.setExamPassCount(passed ? examPassCount + 1 : examPassCount);
            if (isExamCount) {
                eduCourseStatistics.setExamCount(examCount + 1);
            }
        } else {
            //不是第一次考试则找到最后一次考试记录,此时记录已保存寻找上一次的记录
            EduExamRecord lastRecord = eduExamRecordDao.findSecondByUserIdAndExamIdOrderByIdDesc(currentUserId, examId);
            if (lastRecord != null) {
                //上一次通过本次通过则次数不变,本次不通过则减一
                if (lastRecord.getState() == 1) {
                    eduCourseStatistics.setExamPassCount(passed ? examPassCount : examPassCount - 1);
                    //上一次不通过此次通过次数加一,不通过不变
                } else {
                    eduCourseStatistics.setExamPassCount(passed ? examPassCount + 1 : examPassCount);
                }
            }
        }
        eduCourseStatisticsDao.save(eduCourseStatistics);
    }

    @Override
    public ResultBean examCountQuery() {
        EduCourseStatistics statistics = getStatistics(Context.getCurrentUserId());
        return ResultBean.success(statistics);
    }

    private EduCourseStatistics getStatistics(Long userId) {
        EduCourseStatistics statistics = eduCourseStatisticsDao.selectByUserId(userId);
        if (statistics == null) {
            statistics = new EduCourseStatistics();
        }
        // 学习时长
       // EduCourseStatistics eduCourseStatistics = eduCourtTrainningRecordMapper.selectByUserId(userId);
        EduCourseStatistics eduCourseStatistics = eduCourtTrainningRecordMapper.selectByUserId(userId);
        if (eduCourseStatistics != null) {
            statistics.setCourseLearnTime(eduCourseStatistics.getCourseLearnTime());
        }
        // 查询考试总数的逻辑和APP考试记录列表保持一致
        BaseUser user = baseUserDao.getOne(userId);
        Long departId = user.getDepartId();
        Long jobId = user.getJob();
        List<Long> examIds = eduExamUserScopeDao.findUserCanJoinExams(userId, departId, jobId);
        // app端，finished表示已经考过或者已经过期的
        QEduExam q = QEduExam.eduExam;
        List<EduExamRecord> records = eduExamRecordDao.selectByUserIdLatest(userId);
        Set<Long> recordExamIds = records.stream().filter(item -> "0".equals(item.getDeleteFlag())).map(EduExamRecord::getExamId).collect(Collectors.toSet());
        com.querydsl.core.types.Predicate p = ExpressionUtils.and(q.id.in(examIds), q.state.eq(ExamStates.PUBLISHED.getId()));
        p = ExpressionUtils.and(p, q.id.in(recordExamIds).or(q.endTime.lt(new Date())));
        long count = eduExamDao.count(p);
        // 考试总数
        statistics.setExamCount(Integer.valueOf(String.valueOf(count)));
        statistics.setAppLearnTime(calLearnTime(statistics.getCourseLearnTime()));
        return statistics;
    }

    @Override
    public ResultBean examStatistics() {
        Tuple tuple = eduExamRecordDao.findExamCount();
        //(考试人次)取考试的参加人数，重复考试也只算一个人数,没人参加返回0
        Integer userCount = eduExamRecordDao.findUserCount() == null ? 0 : eduExamRecordDao.findUserCount();
        int examRecordCount = tuple.get("examCount", Number.class).intValue();
        //取考试通过的人数(取每个人每场考试的最后一次记录)
        Integer passCount = eduExamRecordDao.findPassCount();
        List<EduExamPassDto> eduExamPassDtoList = Lists.newArrayList();
        if (examRecordCount == 0) {
            eduExamPassDtoList.add(new EduExamPassDto("合格"));
            eduExamPassDtoList.add(new EduExamPassDto("不合格"));
        } else {
            eduExamPassDtoList.add(new EduExamPassDto("合格", passCount));
            eduExamPassDtoList.add(new EduExamPassDto("不合格", userCount - passCount));
        }
        // 考试数量 取的是所有已发布考试
        int examCount = eduExamDao.countByState(ExamStates.PUBLISHED.getId());
        return ResultBean.success(new EduExamCountDto(userCount, examCount, eduExamPassDtoList));
    }

    @Override
    public ResultBean eduExamJoinRate() {
        // 今年的考试，以开始时间为取值点
        int year = LocalDate.now().getYear();
        LocalDateTime startTime = LocalDateTime.of(LocalDate.of(year, 1, 1), LocalTime.of(0, 0, 0));
        LocalDateTime endTime = LocalDateTime.of(LocalDate.of(year, 12, 31), LocalTime.of(23, 59, 59));
        List<EduExam> list = eduExamDao.findByStartTimeBetween(DateTimeUtil.localDateTime2Date(startTime),
                DateTimeUtil.localDateTime2Date(endTime));
        if (CollectionUtils.isEmpty(list)) {
            return ResultBean.defaultSuccessResult();
        }
        List<Long> examIds = list.stream().map(r -> r.getId()).collect(Collectors.toList());
        List<EduExamUserScope> userScopeList = eduExamUserScopeDao.findByExamIdIn(examIds);
        List<Long> userIds = Lists.newArrayList();
        List<Long> jobIds = Lists.newArrayList();
        List<Long> departIds = Lists.newArrayList();
        for (EduExamUserScope scope : userScopeList) {
            String objType = scope.getObjType();
            if (objType.equals(ExamJoinInTargetTypes.USER.getCode())) {
                userIds.add(scope.getObjId());
            } else if (objType.equals(ExamJoinInTargetTypes.DEPART.getCode())) {
                departIds.add(scope.getObjId());
            } else if (objType.equals(ExamJoinInTargetTypes.JOB.getCode())) {
                jobIds.add(scope.getObjId());
            }
        }
        List<BaseUser> allUser = baseUserDao.findAll();
        Map<Long, Long> jobMap = allUser.stream()
                .collect(Collectors.groupingBy(r -> r.getJob(), Collectors.counting()));
        Map<Long, Long> departMap = allUser.stream()
                .collect(Collectors.groupingBy(r -> r.getDepartId(), Collectors.counting()));
        Long totalCount = Long.valueOf(userIds.size());
        for (Long jobId : jobIds) {
            totalCount += jobMap.getOrDefault(jobId, 0L);
        }
        for (Long departId : departIds) {
            totalCount += departMap.getOrDefault(departId, 0L);
        }
        int alreadyCount = eduExamRecordDao.countByExamIdIn(examIds);
        int absentCount = totalCount.intValue() - alreadyCount;
        List<BpWorkApplyDetailDto> applyDetailList = Lists.newArrayList();
        applyDetailList.add(new BpWorkApplyDetailDto("未参考人数", absentCount));
        applyDetailList.add(new BpWorkApplyDetailDto("参考人数", alreadyCount));
        return ResultBean.success(new BpWorkApplyDto(totalCount.intValue(), applyDetailList));

    }

    @Override
    public ResultBean recordQuery(String userName) {
        Assert.notNull(userName, "请输入人员姓名");
        BaseUser baseUser = baseUserDao.findFirstByRealName(userName);
        Assert.notNull(baseUser, "此用户不存在");
        Long userId = baseUser.getId();
        int count = eduExamRecordDao.countByUserId(userId);
        EduCourseStatistics statistics = getStatistics(userId);
        String appLearnTime = statistics.getAppLearnTime();
        return ResultBean.success(new EduExamRecordDto(userId, count, appLearnTime));
    }

    @Override
    public ResultBean examQueryPage(EduExamQueryBean bean) {
        Pageable pageable = PageRequest.of(bean.getPage(), bean.getSize());
        pageable = PageDataUtils.addOrderByDesc(pageable, "startTime");
        com.querydsl.core.types.Predicate p = createQueryCondition(bean);
        final Page<EduExam> exams = eduExamDao.findAll(p, pageable);
        if (CollectionUtils.isEmpty(exams.getContent())) {
            return ResultBean.pageData(Lists.newArrayList(), 0);
        }
        List<Long> examIds = exams.getContent().stream().map(e -> e.getId()).collect(Collectors.toList());
        List<Tuple> examUserCount = eduExamRecordDao.findExamUserCountByGroup(examIds);
        Map<Long, Integer> examUserCountMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(examUserCount)) {
            // return ResultBean.pageData(Lists.newArrayList(), 0);
            examUserCountMap = examUserCount.stream().collect(Collectors.toMap(
                    t -> t.get("exam_id", Number.class).longValue(),
                    t -> t.get("count", Number.class).intValue()
            ));
        }

        List<EduExamRecordPageDto> pageDtoList = Lists.newArrayList();

        List<ResultDto> resultDtoList = eduExamRecordsMapper.examPassCountByExamIds(examIds);
        List<EduSourceDto> scoreList = eduExamRecordsMapper.queryPassScoreOfExamIds(examIds);
        Map<Long, ResultDto> resultDtoMap = resultDtoList.stream().collect(Collectors.toMap(ResultDto::getId, e -> e));
        Map<Long, List<EduSourceDto>> map = scoreList.stream().collect(Collectors.groupingBy(EduSourceDto::getExamId));
        for (EduExam exam : exams) {
            Integer passCount = null;
            String passRate = "0%";
            Integer actualExamCount = examUserCountMap.get(exam.getId());
            if (actualExamCount == null) {
                actualExamCount = 0;
            }
            if (actualExamCount != 0) {
                //每场考试的每个人的最后一次记录
//                passCount = eduExamRecordDao.examPassCount(exam.getId()) == null?0:eduExamRecordDao.examPassCount(exam.getId());
                passCount = resultDtoMap.get(exam.getId()) == null ? 0 : resultDtoMap.get(exam.getId()).getNum();
                passRate = passCount * 100 / actualExamCount + "%";
            }
            int needExamCount = getNeedExamCount(exam.getId());
            int absentExamCount = needExamCount - actualExamCount;

            // 不敢动那个超复杂的sql，单独查询
//            List<Double> scores = testPaperDao.queryPassScoreOfExam(exam.getId());
            List<Double> scores = new ArrayList<>();
            if (map.get(exam.getId()) != null) {
                for (EduSourceDto dto : map.get(exam.getId())) {
                    scores.add(dto.getPassScore());
                }
            }
            pageDtoList.add(
                    new EduExamRecordPageDto(exam.getId(), exam.getName(), exam.getStartTime(), needExamCount, actualExamCount,
                            absentExamCount < 0 ? 0 : absentExamCount, passCount, passRate, formatPassScore(scores), exam.getFormType(), exam.getTrainType()));
        }
        return ResultBean.pageData(pageDtoList, exams.getTotalElements());
    }

    private com.querydsl.core.types.Predicate createQueryCondition(EduExamQueryBean bean) {
        QEduExam q = QEduExam.eduExam;
        com.querydsl.core.types.Predicate p = q.id.isNotNull();
        if (bean.getExamName() != null) {
            p = ExpressionUtils.and(p, q.name.contains(bean.getExamName()));
        }
        if (bean.getExamTime() != null) {
            p = ExpressionUtils.and(p, q.startTime.goe(DateUtils.getDate(bean.getExamTime(), 0)));
            p = ExpressionUtils.and(p, q.startTime.lt(DateUtils.getDate(bean.getExamTime(), 1)));
        }
        p = ExpressionUtils.and(p, q.state.eq(ExamStates.PUBLISHED.getId()));
        return p;
    }

    private String formatPassScore(List<Double> scores) {
        if (scores.isEmpty()) {
            return null;
        }
        scores.removeAll(Collections.singleton(null));
        Double score = null;
        double delta = 0;
        if (!CollectionUtils.isEmpty(scores)) {
            score = scores.get(0);
            delta = Math.abs(score - score.intValue());
            if (delta < 0.1) {
                // 实际是整数
                return score.intValue() + "";
            }
        }
        // 保留一位小数
        return (score != null ? score.intValue() : 0) + "." + Math.round(delta * 10);
    }

    private int getNeedExamCount(Long examId) {
        int needExamCount;
        List<EduExamUserScope> eduExamUserScopes = eduExamUserScopeDao.findByExamId(examId);
        if (CollectionUtils.isEmpty(eduExamUserScopes)) {
            return 0;
        }
        // JDU: U 人员; D 部门; J 岗位 一个考试唯一关联其中一项
        String objType = eduExamUserScopes.get(0).getObjType();
        if ("D".equals(objType)) {
            List<Long> collect = eduExamUserScopes.stream().map(r -> r.getObjId()).collect(Collectors.toList());
            needExamCount = baseUserDao.countByDepartIdIn(collect);
        } else if ("J".equals(objType)) {
            List<Long> objIdByExamId = eduExamUserScopeDao.findObjIdByExamId(examId);
            needExamCount = baseUserDao.countByJobIn(objIdByExamId);
        } else {
            needExamCount = eduExamUserScopes.size();
        }
        return needExamCount;
    }

    @Override
    public ResultBean examRecordDetail(Long examId, SearchEduExamRecordDetailBean condition, Pageable pageable) {
        Assert.notNull(examId, "examId cannot be null");
        QEduExamRecord record = QEduExamRecord.eduExamRecord;
        QBaseUser user = QBaseUser.baseUser;
        JPAQuery<Long> subQuery = jpaQueryFactory.select(record.id.max()).from(record).where(record.examId.eq(examId))
                .groupBy(record.userId);
        if (condition.getIsTryAgain() != null) {
            if ("Y".equals(condition.getIsTryAgain())) {
                // 存在多条记录
                subQuery.having(record.count().gt(1));
            } else {
                // 只有一条记录
                subQuery.having(record.count().eq(1L));
            }
        }
        List<Long> avaliableList = subQuery.createQuery().getResultList();
        com.querydsl.core.types.Predicate p = ExpressionUtils.and(record.examId.eq(examId),
                record.id.in(avaliableList));
        if (condition.getName() != null) {
            p = ExpressionUtils.and(p, user.realName.contains(condition.getName()));
        }
        if (condition.getPassed() != null) {
            if ("Y".equals(condition.getPassed())) {
                p = ExpressionUtils.and(p, record.state.eq(1));
            } else {
                p = ExpressionUtils.and(p, record.state.eq(0));
            }
        }
        if (condition.getDepartId() != null) {
            p = ExpressionUtils.and(p, user.departId.eq(condition.getDepartId()));
        }
        if (condition.getStartScore() != null) {
            p = ExpressionUtils.and(p, record.score.goe(condition.getStartScore()));
        }
        if (condition.getEndScore() != null) {
            p = ExpressionUtils.and(p, record.score.loe(condition.getEndScore()));
        }
        JPAQuery<com.querydsl.core.Tuple> master = jpaQueryFactory.select(record, user).from(record).leftJoin(user)
                .on(record.userId.eq(user.id)).where(p, record.score.ne("未考")).offset(pageable.getOffset()).limit(pageable.getPageSize());
        if (pageable.getSort() != null) {
            processSort(master, pageable.getSort(), user, record);
        }
        long count = master.fetchCount();
        List<com.querydsl.core.Tuple> results = master.fetch();

        // Page<EduExamRecord> page = eduExamRecordDao.pageByExamId(examId, pageable);
        List<EduExamJoinUserListBean> data = results.stream().map(e -> {
            EduExamRecord f = e.get(record);
            BaseUser u = e.get(user);
            EduExamJoinUserListBean bean = new EduExamJoinUserListBean();
            bean.setId(f.getId());
            bean.setUserName(u.getRealName());
            if (u.getState() == null || u.getState() == 1) {
                bean.setUserByType("正常");
            }
            if (u.getState() != null && u.getState() == 44) {
                bean.setUserByType("已删除");
            }
            if (f.getScore().equals("未考")) {
                f.setScore("");
            }
            bean.setUserId(f.getUserId());
            bean.setScore(f.getScore());
            bean.setJobName(bpJobDao.findNameById(u.getJob()));
            bean.setDepartName(bpDepartDao.getNameById(u.getDepartId()));
            Optional<EduExam> examOptional = eduExamDao.findById(f.getExamId());
            if (examOptional.isPresent()) {
                EduExam eduExam = examOptional.get();
                bean.setExamName(eduExam.getName());
                bean.setShowAutograph(eduExam.getShowAutograph());
            }
            // 判断是否是主观题 1 是 0 否
            bean.setIsSubjectiveQuestion(f.getIsCheck() > 0 ? 1 : 0);
            bean.setKgScore(getScore(f.getId()).get("kgScore"));
            bean.setZgScore(getScore(f.getId()).get("zgScore"));
            bean.setSubmitTime(f.getSubmitTime());
            bean.setPassState(Objects.equal(1, f.getState()) ? "合格" : "不合格");
            bean.setMoreThanOnce((f.getSubmitTimes() == null || f.getSubmitTimes() <= 1) ? "否" : "是");
            if (f.getCostTime() != null) {
                String costTime = DateUtils.getFormatCostTime(f.getCostTime());
                bean.setCostTime(costTime);
            }
            bean.setAutograph(f.getAutograph());
            return bean;
        }).collect(Collectors.toList());

        //成绩改为字符串类型 排序失效
        if (pageable.getSort()!=null){
            String sort = pageable.getSort().toString();
            //passState: DESC
            if (sort.startsWith("passState") || sort.startsWith("score")){
                String[] sorts = sort.split(":");
                if (" DESC".equals(sorts[1])){
                    data = data.stream().sorted(Comparator.comparing(s -> Integer.valueOf(s.getScore()), Comparator.reverseOrder())).collect(Collectors.toList());
                }else {
                    data = data.stream().sorted(Comparator.comparing(s -> Integer.valueOf(s.getScore()))).collect(Collectors.toList());
                }
            }
        }
        return ResultBean.pageData(data, count);
    }

    @Override
    public ResultBean learnerCourseStatistics(Long courseId, BpUserSearchBean condition, Pageable pageable) {
        Page<BaseUserFindAllBean> page;
        if (StrUtil.isNotBlank(condition.getStudyProgress())) {
            int end = baseUserDao.getEffectiveUserCount();
            PageRequest request = PageRequest.of(0, end + 1);
            page = baseUserService.findByPage(request, condition, null, "ASC", "createTime");
        } else {
            page = baseUserService.findByPage(pageable, condition, null, "ASC", "createTime");
        }
        List<BaseUserFindAllBean> allBeanList = page.getContent();
        if (page == null || CollectionUtils.isEmpty(allBeanList)) {
            return ResultBean.pageData(ListUtil.empty(), 0);
        }
        // 需要学习的课件
        List<Long> coursewareIdsByCourseId = eduCourseCoursewareDao.findCoursewareIdsByCourseId(courseId);
        List<Long> deptIds = allBeanList.stream().map(BaseUserFindAllBean::getDepartId).collect(Collectors.toList());
        List<Long> userIds = allBeanList.stream().map(BaseUserFindAllBean::getId).collect(Collectors.toList());
        List<BpDepart> deptList = bpDepartDao.findByIdIn(deptIds);
        List<EduCourseStatistics> courseList = eduCourseStatisticsDao.findCoursesByUserIds(userIds);
        Map<Long, String> deptMap = new HashMap<>();
        Map<Long, EduCourseStatistics> courseMap = new HashMap<>();
        deptList.forEach(dept -> {
            deptMap.put(dept.getId(), dept.getName());
        });
        courseList.forEach(course -> {
            courseMap.put(course.getUserId(), course);
        });
        if (CollectionUtils.isEmpty(coursewareIdsByCourseId)) {
            for (BaseUserFindAllBean baseUserFindAllBean : allBeanList) {
                baseUserFindAllBean.setStudyCourseProgressed("未学习");
                baseUserFindAllBean.setStudyProgressed(1);
                baseUserFindAllBean.setDepartName(deptMap.get(baseUserFindAllBean.getDepartId()));
            }
        } else {
            for (BaseUserFindAllBean baseUserFindAllBean : allBeanList) {
                // 赋值学习进度
                userLearnerCourseProgressed(baseUserFindAllBean, coursewareIdsByCourseId, courseMap.get(baseUserFindAllBean.getId()), baseUserFindAllBean.getId());
                baseUserFindAllBean.setDepartName(deptMap.get(baseUserFindAllBean.getDepartId()));
            }
        }
        List<BaseUserFindAllBean> sortList = allBeanList.stream().sorted(Comparator.comparing(BaseUserFindAllBean::getStudyProgressed).reversed()).collect(Collectors.toList());
        if (StrUtil.isNotBlank(condition.getStudyProgress())) {
            // 筛选条件
            List<BaseUserFindAllBean> findAllBeanList = sortList.stream().filter(v -> v.getStudyCourseProgressed().equals(condition.getStudyProgress())).collect(Collectors.toList());
            // 分页
            List<BaseUserFindAllBean> pageList = ListUtil.page(pageable.getPageNumber(), pageable.getPageSize(), findAllBeanList);
            return ResultBean.pageData(pageList, findAllBeanList.size());
        }
        log.info("开始时间4---->" + System.currentTimeMillis());
        return ResultBean.pageData(sortList, page.getTotalElements());
    }


    @Override
    public void learnerCourseStatisticsExport(Long courseId, BpUserSearchBean condition, HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(courseId, "courseId cannot be null");

        EduCourse eduCourse = eduCourseService.findById(courseId);
        // 所有人员考试详情
        List<BaseUserFindAllBean> dataList = (List<BaseUserFindAllBean>) learnerCourseStatistics(courseId, condition, PageRequest.of(0, 99999)).getData();
        String file = eduCourse.getName() + "学员统计" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + ".xls".trim();
        response.setContentType(request.getServletContext().getMimeType(file));
        HSSFWorkbook hwb = null;
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(file.getBytes("GBK"), "iso8859-1"));
            List<String> cellNames = Arrays.asList("姓名", "工号", "部门", "学习进度");
            Pair<HSSFWorkbook, HSSFSheet> pair = PoiUtil.getPoi("课程统计", cellNames);
            addCourseRecordData(dataList, pair);
            hwb = pair.getKey();
            hwb.write(response.getOutputStream());
        } catch (IOException e) {
            log.warn("课程统计excel导出失败：" + e.getMessage());
            throw new IllegalArgumentException("excel导出失败，请联系系统管理员");
        } finally {
            if (hwb != null) {
                try {
                    hwb.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public ResultBean learnDocument(Long courseawareId, Integer learnTime, Integer isReturn, String uuid, Long trainingId) {
        Assert.isTrue(learnTime != null && learnTime > 0, "学习时间不符合要求");
        Long currentUserId = Context.getCurrentUserId();
        if (isReturn == 1) {
            learnTime = addLearnHistory(currentUserId, learnTime, courseawareId, Context.getCurrentTenantId(), Context.getCurrentOrgCode(), uuid, trainingId);
            saveCourseStatistics(currentUserId, learnTime, courseawareId, trainingId);
        } else {
            saveEduCourseRedisDto(currentUserId, learnTime, courseawareId, uuid, Context.getCurrentTenantId(), Context.getCurrentOrgCode(), trainingId);
        }
        return ResultBean.defaultSuccessResult();
    }

    /**
     * 保存统计
     *
     * @param currentUserId
     * @param learnTime
     * @param courseawareId
     */
    public void saveCourseStatistics(Long currentUserId, Integer learnTime, Long courseawareId, Long trainingId) {
        // EduCourseLearnHistory learnHistory = historyDao.findByCourseawareIdAndUserId(courseawareId, currentUserId);
        EduCourseLearnHistory learnHistory = eduCourseLearnHistoryMapper.findByCourseawareIdAndUserId(courseawareId, currentUserId, trainingId);
        EduCourseStatistics eduCourseStatistics = eduCourseStatisticsDao.selectByUserId(currentUserId);
        if (eduCourseStatistics == null) {
            eduCourseStatistics = new EduCourseStatistics();
            eduCourseStatistics.setCourseLearnTime(Long.valueOf(learnTime));
            eduCourseStatistics.setUserId(Context.getCurrentUserId());
            List<Long> longs = new ArrayList<>();
            longs.add(courseawareId);
            eduCourseStatistics.setCoursewareLearned(JSON.toJSONString(longs));
            eduCourseStatistics.setCourseLearnTime(learnTime.longValue());
        } else {
            String coursewareLearned = eduCourseStatistics.getCoursewareLearned();
            List<Long> longs = JSON.parseArray(coursewareLearned, Long.class);
            longs = longs == null ? Lists.newArrayList() : longs;
            if (!longs.contains(courseawareId)) {
                longs.add(courseawareId);
                eduCourseStatistics.setCoursewareLearned(JSON.toJSONString(longs));
            }
            // 实行累加
            if (learnHistory == null || (learnHistory != null && learnHistory.getState() == 0)) {
                Long courseLearnTime = eduCourseStatistics.getCourseLearnTime();
                eduCourseStatistics.setCourseLearnTime(courseLearnTime + learnTime);
            }
        }
        eduCourseStatisticsDao.save(eduCourseStatistics);
    }

    /**
     * 保存数据导redis缓存
     *
     * @param currentUserId
     * @param learnTime
     * @param courseawareId
     * @param uuid
     * @param tenentId
     * @param orgCode
     */
    private void saveEduCourseRedisDto(Long currentUserId, Integer learnTime, Long courseawareId, String uuid, Long tenentId, String orgCode, Long trainingId) {
        RList<Object> list = redisClient.getList(WWYT_LEARN_DOCUMENT);
        boolean isFirst = true;
        if (list != null && !list.isEmpty()) {
            for (int i = 0; i < list.size(); i++) {
                EduCourseRedisDto dto = JSON.parseObject(list.get(i).toString(), EduCourseRedisDto.class);
                if (dto.getUserId().equals(currentUserId) && dto.getCourseawareId().equals(dto.getCourseawareId()) && uuid.equals(dto.getUuid())) {
                    learnTime = dto.getAppLearnTime() + learnTime;
                    dto.setAppLearnTime(learnTime);
                    redisClient.updateList(WWYT_LEARN_DOCUMENT, i, JSON.toJSONString(dto));
                    isFirst = false;
                    break;
                }
            }
        }
        if (isFirst) {
            redisClient.setList(WWYT_LEARN_DOCUMENT, JSON.toJSONString(new EduCourseRedisDto(currentUserId, learnTime, courseawareId, uuid, LocalDateTime.now(), tenentId, orgCode, trainingId)));
        }
    }

    @Override
    public void saveLearnHistory() {
        RList<Object> list = redisClient.getList(WWYT_LEARN_DOCUMENT);
        if (list != null && !list.isEmpty()) {
            log.info("生成文档学习记录数量-->" + list.size());
            for (int i = 0; i < list.size(); i++) {
                EduCourseRedisDto dto = JSON.parseObject(list.get(i).toString(), EduCourseRedisDto.class);
                EduCourseLearnHistory history = new EduCourseLearnHistory();
                history.setUserId(dto.getUserId());
                history.setCourseawareId(dto.getCourseawareId());
                history.setLastTime(dto.getAppLearnTime());
                history.setAppLearnTime(dto.getAppLearnTime());
                history.setState(0);
                history.setCreateTime(dto.getTime());
                history.setUpdateTime(dto.getTime());
                history.setTenentId(dto.getTenentId());
                history.setOrgCode(dto.getOrgCode());
                historyDao.save(history);
                redisClient.removeListValue(WWYT_LEARN_DOCUMENT, JSON.toJSONString(dto));
                saveCourseStatistics(dto.getUserId(), dto.getAppLearnTime(), dto.getCourseawareId(), null);
            }
        }
    }

    /**
     * 生成学习记录
     *
     * @param currentUserId
     * @param learnTime
     * @param courseawareId
     * @param tenentId
     * @param orgCode
     */
    private Integer addLearnHistory(Long currentUserId, Integer learnTime, Long courseawareId, Long tenentId, String orgCode, String uuid, Long trainingId) {
        Integer state = 0;
        RList<Object> list = redisClient.getList(WWYT_LEARN_DOCUMENT);
        if (list != null && !list.isEmpty()) {
            for (int i = 0; i < list.size(); i++) {
                EduCourseRedisDto dto = JSON.parseObject(list.get(i).toString(), EduCourseRedisDto.class);
                if (dto.getUserId().equals(currentUserId) && uuid.equals(dto.getUuid())) {
                    learnTime = dto.getAppLearnTime() + learnTime;
                    redisClient.removeListValue(WWYT_LEARN_DOCUMENT, JSON.toJSONString(dto));
                }
            }
        }
        int finish = eduCoursewareMapper.selectCowrseFinish(courseawareId, currentUserId, learnTime, trainingId);
        if (finish == 1) state = 1;

        EduCourseLearnHistory history = new EduCourseLearnHistory();
        history.setUserId(currentUserId);
        if (!ObjectUtils.isEmpty(trainingId)) {
            history.setTrainningRecordId(trainingId);
        }
        history.setCourseawareId(courseawareId);
        history.setLastTime(learnTime);
        history.setAppLearnTime(learnTime);
        history.setState(state);
        history.setTenentId(tenentId);
        history.setOrgCode(orgCode);
        history.setCreateTime(LocalDateTime.now());
        history.setUpdateTime(LocalDateTime.now());
        EduCourseLearnHistory save = historyDao.save(history);

        if (state == 1 && !ObjectUtils.isEmpty(trainingId)) {
            EduTrainningRecord eduTrainningRecord = eduTrainningRecordMapper.selectByIdUnFinished(trainingId);
            if (!ObjectUtils.isEmpty(eduTrainningRecord)) {
                List<Long> userIds = new ArrayList<>();
                userIds.add(currentUserId);
                //培训合格标准为学完课程
                if (TrainingQualifiedStandard.FINISH_COURSE.getId() == eduTrainningRecord.getTrainingQualifiedStandard()) {
                    //用户是否学完课程
                    if (isFinishCourse(currentUserId, eduTrainningRecord.getId())) {
                        eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "Y", userIds);
                    }
//                        else {
//                            eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "N", userIds);
//                        }
                } else if (TrainingQualifiedStandard.FINISH_COURSE_EXAM.getId() == eduTrainningRecord.getTrainingQualifiedStandard()) {
                    //用户是否学完课程并且考试是否通过
                    if (isFinishCourse(currentUserId, eduTrainningRecord.getId())
                            && eduTrainningRecordUserMapper.selectPassExamByUserId(currentUserId, eduTrainningRecord.getExamId()) > 0) {
                        eduTrainningRecordUserMapper.updateTrainingResultByUserIds(eduTrainningRecord.getId(), "Y", userIds);
                    }
                }
            } else {
                save.setState(0);
                eduCourseLearnHistoryMapper.updateById(save);
            }
        }
        return learnTime;
    }


    public void userLearnerCourseProgressed(BaseUserFindAllBean bean, List<Long> coursewareIdsByCourseId, EduCourseStatistics eduCourseStatistics, Long userId) {
        String progressed = "未学习";
        bean.setStudyProgressed(1);
        // 用户已学习课件统计
        if (eduCourseStatistics != null && StrUtil.isNotBlank(eduCourseStatistics.getCoursewareLearned())) {
            // 已学习的课件ids
            List<Long> coursewareIds = JSONUtil.toList(eduCourseStatistics.getCoursewareLearned(), Long.class);
            if (!CollectionUtils.isEmpty(coursewareIds)) {
                filterData(coursewareIds, userId);
            }
            List<Long> longList = ListUtils.retainAll(coursewareIds, coursewareIdsByCourseId);
            if (longList.size() > 0) {
                progressed = "学习中";
                bean.setStudyProgressed(2);
            }
            if (coursewareIds.containsAll(coursewareIdsByCourseId)) {
                progressed = "学习完毕";
                bean.setStudyProgressed(3);
            }

        }
        bean.setStudyCourseProgressed(progressed);
    }

    public void filterData(List<Long> coursewareIds, Long userId) {
        //视频课件集合
        List<Long> videoIds = eduCoursewareDao.findVideo(coursewareIds);
        if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(videoIds)) {
            coursewareIds.removeAll(videoIds);
            for (Long videoId : videoIds) {
                Integer num = eduCourseLearnHistoryDao.findByIdsAndState(videoId, userId);
                if (num != null && num > 0) {
                    coursewareIds.add(videoId);
                }
            }
        }
    }

    public void addCourseRecordData(List<BaseUserFindAllBean> dataList, Pair<HSSFWorkbook, HSSFSheet> pair) {
        HSSFSheet sheet = pair.getValue();
        int index = 1;
        for (int i = 0; i < dataList.size(); i++) {
            BaseUserFindAllBean dto = dataList.get(i);
            HSSFRow row = sheet.createRow(i + 1);
            row = sheet.createRow(i + 1);
            row.createCell(0).setCellValue(dto.getName());
            row.createCell(1).setCellValue(dto.getCode());
            row.createCell(2).setCellValue(dto.getDepartName());
            row.createCell(3).setCellValue(dto.getStudyCourseProgressed());
        }
    }

    /**
     * 考试用时（毫秒数）转化为xx分xx秒 格式
     */
    private String getFormatCostTime(Long millisecond) {
        StringBuilder costTime = new StringBuilder();
        long minute = millisecond / (1000 * 60);
        long second = (millisecond % (1000 * 60)) / 1000;
        if (minute > 0) {
            costTime.append(minute).append("分");
        }
        if (second > 0) {
            costTime.append(second).append("秒");
        }
        return costTime.toString();
    }

    private void processSort(JPAQuery<com.querydsl.core.Tuple> master, Sort sort, QBaseUser user,
                             QEduExamRecord record) {
        processOrderItem("usrName", master, sort, user.realName);
        processOrderItem("submitTime", master, sort, record.submitTime);
        processOrderItem("score", master, sort, record.score);
        processOrderItem("usrName", master, sort, user.realName);
        processOrderItem("passState", master, sort, record.score);
        processOrderItem("costTime", master, sort, record.costTime);
        processOrderItem("moreThanOnce", master, sort, record.submitTime);
    }

    void processOrderItem(String property, JPAQuery<com.querydsl.core.Tuple> master, Sort sort,
                          ComparableExpressionBase path) {
        Order o = sort.getOrderFor(property);
        if (o != null) {
            master.orderBy(o.isAscending() ? path.asc() : path.desc());
        }
    }

    @Override
    public EduExamJoinUserListBean getExamRecordUserInfo(Long examRecordId) {
        EduExamRecord f = eduExamRecordDao.getOne(examRecordId);
        EduExamJoinUserListBean bean = new EduExamJoinUserListBean();
        bean.setId(f.getId());
        BaseUser baseUser = baseUserDao.getOne(f.getUserId());
        bean.setUserName(baseUser.getRealName());
        bean.setDepartName(bpDepartDao.findDepartNameById(baseUser.getDepartId()));
        bean.setJobName(bpJobDao.findNameById(baseUser.getJob()));
        EduExam eduExam = eduExamDao.findById(f.getExamId()).get();
        if (eduExam != null) {
            bean.setExamName(eduExam.getName());
            bean.setShowAutograph(eduExam.getShowAutograph());
        }
        bean.setUserId(f.getUserId());
        bean.setScore(f.getScore());
        bean.setKgScore(getScore(examRecordId).get("kgScore"));
        bean.setZgScore(getScore(examRecordId).get("zgScore"));
        bean.setIsSubjectiveQuestion(f.getIsCheck() > 0 ? 1 : 0);
        bean.setSubmitTime(f.getSubmitTime());
        bean.setPassState(Objects.equal(1, f.getState()) ? "及格" : "不及格");
        bean.setMoreThanOnce((f.getSubmitTimes() == null || f.getSubmitTimes() <= 1) ? "否" : "是");
        bean.setAutograph(f.getAutograph());
        return bean;
    }


    public Map<String, Float> getScore(Long examRecordId) {
        Map<String, Float> map = new HashMap<>();
        float zgScore = 0;
        float kgScore = 0;
        List<EduExamUserAnswer> userAnswerList = eduExamUserAnswerDao.findByExamRecordId(examRecordId);
        for (EduExamUserAnswer eduExamUserAnswer : userAnswerList) {
            EduQuestion question = eduQuestionDao.getOne(eduExamUserAnswer.getQuestionId());
            // 主观题
            if (question.getType().equals(QuestionTypes.ZG_CHOICE.getCode())) {
                zgScore += eduExamUserAnswer.getScore() == null ? 0 : eduExamUserAnswer.getScore();
            } else {
                // 客观题
                kgScore += eduExamUserAnswer.getScore();
            }
        }
        map.put("zgScore", zgScore);
        map.put("kgScore", kgScore);
        return map;
    }

    @Override
    public ResultBean courseList(Pageable pageable) {
        return null;
    }

    @Override
    public List<Long> selectCoursewareLearnedByUserId(Long userId) {
        EduCourseStatistics courseStatistics = eduCourseStatisticsDao.selectByUserId(userId);
        if (courseStatistics != null) {
            String coursewareLearned = courseStatistics.getCoursewareLearned();
            if (StrUtil.isNotBlank(coursewareLearned)) {
                return JSON.parseArray(coursewareLearned, Long.class);
            }
        }
        return null;
    }

    @Override
    public ResultBean allStudyQuery(Pageable pageable) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<EduCourseStatistics> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageable.getPageNumber() + 1, pageable.getPageSize());
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<EduCourseStatistics> eduCourseStatisticsPage = eduCourtTrainningRecordMapper.findAllList(page);
        List<EduCourseStatistics> records = eduCourseStatisticsPage.getRecords();
        for (EduCourseStatistics statistics : records) {
            statistics.setAppLearnTime(calLearnTime(statistics.getCourseLearnTime()));
        }
        return ResultBean.pageData(records, page.getTotal());
    }

    @Override
    public ResultBean userExamRecordPage(Long userId, Pageable pageable) {
        Assert.notNull(userId, "userId cannot be null");
        BaseUser baseUser = baseUserDao.findById(userId).orElseThrow(() -> new IllegalArgumentException("此用户不存在"));
        String departName = bpDepartDao.getNameById(baseUser.getDepartId());
        String jobName = bpJobDao.getNameById(baseUser.getJob());
        List<Tuple> tuples = eduExamRecordDao.userExamRecordPage(userId);
        List<Tuple> resultList = PageableUtil.getList(pageable, tuples);
        List<EduExamUserRecordDto> recordDtoList = new ArrayList<>(resultList.size());
        for (Tuple tuple : resultList) {
            String code = tuple.get("code", String.class);
            String examName = tuple.get("examName", String.class);
            String examType = tuple.get("examType", String.class);
            Integer examTotalTime = tuple.get("examTotalTime", Number.class).intValue();
            Float totalScore = tuple.get("totalScore", Number.class).floatValue();
            Float passScore = tuple.get("passScore", Number.class).floatValue();
            Integer questionCount = tuple.get("questionCount", Number.class).intValue();
            Date examTime = tuple.get("examTime", Date.class);
            String scoreString = tuple.get("score", String.class);
            Float score = Float.valueOf(scoreString);
            String examResult = tuple.get("examResult", String.class);
            recordDtoList.add(new EduExamUserRecordDto(code, examName, examType, examTotalTime, totalScore, passScore,
                    questionCount, examTime, score, examResult));
        }
        return ResultBean.success(
                new EduExamUserTotalDto(baseUser.getRealName(), departName, jobName, recordDtoList, tuples.size()));
    }


    @Override
    public ResultBean userExamRecordNewPage(Long userId, Pageable pageable) {
        ResultBean rb = new ResultBean();
        Assert.notNull(userId, "userId cannot be null");
        BaseUser baseUser = baseUserDao.findById(userId).orElseThrow(() -> new IllegalArgumentException("此用户不存在"));
        String departName = bpDepartDao.getNameById(baseUser.getDepartId());
        String jobName = bpJobDao.getNameById(baseUser.getJob());
        List<Tuple> tuples = eduExamRecordDao.userExamRecordPage(userId);
        List<Tuple> resultList = PageableUtil.getList(pageable, tuples);
        List<EduExamUserRecordDto> recordDtoList = new ArrayList<>(resultList.size());
        for (Tuple tuple : resultList) {
            String code = tuple.get("code", String.class);
            String examName = tuple.get("examName", String.class);
            String examType = tuple.get("examType", String.class);
            Integer examTotalTime = tuple.get("examTotalTime", Number.class).intValue();
            Float totalScore = tuple.get("totalScore", Number.class).floatValue();
            Float passScore = tuple.get("passScore", Number.class).floatValue();
            Integer questionCount = tuple.get("questionCount", Number.class).intValue();
            Date examTime = tuple.get("examTime", Date.class);
            String scoreString = tuple.get("score", String.class);
            Float score = Float.valueOf(scoreString);
            String examResult = tuple.get("examResult", String.class);
            recordDtoList.add(new EduExamUserRecordDto(code, examName, examType, examTotalTime, totalScore, passScore,
                    questionCount, examTime, score, examResult));
        }
        EduExamUserTotalDto eduExamUserTotalDto = new EduExamUserTotalDto(baseUser.getRealName(), departName, jobName, recordDtoList, tuples.size());
        rb.setData(eduExamUserTotalDto);
        rb.setLength((long) tuples.size());
        return rb;
    }

    @Override
    public ResultBean examAbsentQuery(Long examId) {
        Assert.notNull(examId, "examId cannot be null");
        List<Long> needIds = getAbsentUsers(examId);
        if (CollectionUtils.isEmpty(needIds)) {
            return ResultBean.wrapSuccessfulResult("当前考试没有缺考人员");
        }
        List<EduExamAbsentDto> dtoList = baseUserDao.findAbsentByUserIdIn(needIds);
        return ResultBean.success(dtoList);
    }

    private List<Long> getAbsentUsers(Long examId) {
        List<EduExamUserScope> list = eduExamUserScopeDao.findByExamId(examId);
        String objType = list.get(0).getObjType();
        List<Long> needIds;
        if ("D".equals(objType)) {
            List<Long> collect = list.stream().map(r -> r.getObjId()).collect(Collectors.toList());
            needIds = baseUserDao.findIdByDepartIdIn(collect);
        } else if ("J".equals(objType)) {
            List<Long> collect = list.stream().map(r -> r.getObjId()).collect(Collectors.toList());
            needIds = baseUserDao.findIdByJobIn(collect);
        } else {
            needIds = list.stream().map(r -> r.getObjId()).collect(Collectors.toList());
        }
        List<Long> actualIds = eduExamRecordDao.findUserIdByExamId(examId);
        EduExam one = eduExamDao.getOne(examId);
        if (one.getFormType() == 0) {
            List<Long> actualId = eduExamRecordDao.findUserIdByScore(examId);
            needIds.removeAll(actualId);
        } else {
            needIds.removeAll(actualIds);
        }
        return needIds;
    }

    @Override
    public void examRecordExcel(Long examId, HttpServletRequest request, HttpServletResponse response) {
        Assert.notNull(examId, "examId cannot be null");
        // 所有人员考试详情
        EduExamDto examDto = findRecordExcelByExamId(examId);
        String file = "教育考试成绩单.xls";
        response.setContentType(request.getServletContext().getMimeType(file));
        HSSFWorkbook hwb = null;
        try {
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(new String(file.getBytes(), "UTF-8")));
            List<String> cellNames = getCellName(examDto.getFormType());
            Pair<HSSFWorkbook, HSSFSheet> pair = PoiUtil.getPoi("成绩单", cellNames);
            addRecordData(examDto, pair);
            hwb = pair.getKey();
            hwb.write(response.getOutputStream());
        } catch (IOException e) {
            log.warn("教育考试excel导出失败：" + e.getMessage());
            throw new IllegalArgumentException("excel导出失败，请联系系统管理员");
        } finally {
            if (hwb != null) {
                try {
                    hwb.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public ResultBean learnHistory(EduCourseLearnHistoryBean bean) {
        Pageable pageable = PageRequest.of(bean.getPage(), bean.getSize());
        pageable = PageDataUtils.addOrderByDesc(pageable, "id");
        Specification<EduCourseLearnHistory> querySpecifi = (Root<EduCourseLearnHistory> root,
                                                             CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (bean.getUserId() != null) {
                predicates.add(cb.equal(root.get("userId"), bean.getUserId()));
            }
            if (bean.getCourseawareId() != null) {
                predicates.add(cb.equal(root.get("courseawareId"), bean.getCourseawareId()));
            }
            if (bean.getExamTime() != null) {
                LocalDate examTime = bean.getExamTime();
                LocalDateTime startTime = LocalDateTime.of(examTime, LocalTime.of(0, 0, 0));
                LocalDateTime endTime = LocalDateTime.of(examTime, LocalTime.of(23, 59, 59));
                predicates.add(cb.between(root.get("createTime"), startTime, endTime));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<EduCourseLearnHistory> page = historyDao.findAll(querySpecifi, pageable);
        List<EduCourseLearnHistory> list = page.getContent();
        List<Long> userIds = list.stream().map(r -> r.getUserId()).collect(Collectors.toList());
        List<Long> courseawareIds = list.stream().map(r -> r.getCourseawareId()).collect(Collectors.toList());
        Map<Long, String> userMap = baseUserDao.findAllById(userIds).stream()
                .collect(Collectors.toMap(BaseUser::getId, BaseUser::getRealName));
        Map<Long, String> courseMap = coursewareDao.findAllById(courseawareIds).stream()
                .collect(Collectors.toMap(EduCourseware::getId, EduCourseware::getName));
        for (EduCourseLearnHistory history : list) {
            history.setCourseawareName(courseMap.get(history.getCourseawareId()));
            history.setUserName(userMap.get(history.getUserId()));
        }
        return ResultBean.pageData(list, page.getTotalElements());
    }

    @Override
    public ResultBean userLearnHistory(EduLearnHistoryBean bean) {
        Pageable pageable = PageRequest.of(bean.getPage(), bean.getSize());
//        pageable = PageDataUtils.addOrderByDesc(pageable, "createTime");
        List<EduCourseLearnHistory> oldList = historyDao.findHistoryByUserId(bean.getUserId());
//        List<EduCourseLearnHistory> list = page.getContent();
        int end = oldList.size();
        if (oldList.size() > (bean.getPage() + 1) * bean.getSize()) {
            end = (bean.getPage() + 1) * bean.getSize();
        }
        List<EduCourseLearnHistory> list = oldList.subList(bean.getPage() * bean.getSize(), end);

        List<Long> courseawareIds = list.stream().map(r -> r.getCourseawareId()).collect(Collectors.toList());
        List<EduCourseware> eduCoursewareList = coursewareDao.findAllById(courseawareIds);
        Map<Long, EduCourseware> map = eduCoursewareList.stream()
                .collect(Collectors.toMap(EduCourseware::getId, Function.identity()));
        List<Long> typeIdList = eduCoursewareList.stream().map(r -> r.getTypeId()).collect(Collectors.toList());
        Map<Long, String> typeMap = commonTypeDao.findAllById(typeIdList).stream()
                .collect(Collectors.toMap(CommonType::getId, CommonType::getName));
        List<EduLearnHistoryDto> resultList = Lists.newArrayList();
        for (EduCourseLearnHistory history : list) {
            Long courseawareId = history.getCourseawareId();
            EduCourseware eduCourseware = map.get(courseawareId);
            if (eduCourseware == null) {
                continue;
            }
            String typeName = typeMap.get(eduCourseware.getTypeId());
            EduLearnHistoryDto dto = new EduLearnHistoryDto();
            dto.setId(history.getId());
            dto.setCourseawareName(eduCourseware == null ? "" : eduCourseware.getName());
            dto.setCourseawareType(typeName == null ? "" : typeName);
            dto.setLearnDate(history.getCreateTime().toLocalDate());
            Integer minute = eduCourseware.getMinute();
            Integer second = eduCourseware.getSecond();
            if (minute == null && second == null) {
                dto.setLearnProgress("100%");
            } else {
                second = second == null ? 0 : second;
                Integer needSecond = minute == null ? second : minute * 60 + second;
                Integer appLearnTime = history.getAppLearnTime();
                int percent = appLearnTime * 100 / needSecond;
                dto.setLearnProgress(percent > 100 ? "100%" : percent + "%");
            }
            resultList.add(dto);
        }

        return ResultBean.pageData(resultList, oldList.size());
    }

    private void addRecordData(EduExamDto eduExamDto, Pair<HSSFWorkbook, HSSFSheet> pair) {
        HSSFSheet sheet = pair.getValue();
        List<EduRecordExcelDto> resultDtoList = eduExamDto.getResultDtoList();
        int index = 1;
        for (int i = 0; i < resultDtoList.size(); i++) {
            EduRecordExcelDto dto = resultDtoList.get(i);
            HSSFRow row = sheet.createRow(i + 1);
            row = sheet.createRow(i + 1);
            row.createCell(0).setCellValue(index++);
            row.createCell(1).setCellValue(dto.getUserName());
            row.createCell(2).setCellValue(dto.getUserCode());
            row.createCell(3).setCellValue(dto.getExamName());
            row.createCell(8).setCellValue(dto.getExamTotalTime().toString());
            Integer state = dto.getState();
            row.createCell(4).setCellValue(state == null ? "" : dto.getScore().toString());
            row.createCell(5).setCellValue(dto.getTotalScore().toString());
            row.createCell(6).setCellValue(state == null ? "" : (state == 1 ? "通过" : "不通过"));
            row.createCell(7).setCellValue(dto.getExamType());
            row.createCell(9).setCellValue(state == null ? "否" : "是");
            if (eduExamDto.getFormType() != null && eduExamDto.getFormType() == ExamType.FORM_ONE.type) {
                row.createCell(10).setCellValue(dto.getActualTime());
                row.createCell(11)
                        .setCellValue(state == null ? "" : SimpleDateFormatCache.getYmdhms().format(dto.getSubmitTime()));
                row.createCell(12).setCellValue(dto.getSubmitTimes());
                row.createCell(13).setCellValue(dto.getRightCount() == null ? 0 : dto.getRightCount());
                row.createCell(14).setCellValue(dto.getWrongCount() == null ? 0 : dto.getWrongCount());
                row.createCell(15).setCellValue(dto.getZgScore() == null ? "无" : dto.getZgScore() + "");
            }
        }
    }

    private List<String> getCellName(Integer formType) {
        List<String> list = Lists.newArrayList();
        list.add("序号");
        list.add("考试人员");
        list.add("人员工号");
        list.add("考试名称");
        list.add("得分");
        list.add("总分");
        list.add("状态");
        list.add("考试类型");
        list.add("考试时长");
        list.add("是否参加考试");
        if (formType == ExamType.FORM_ONE.type) {
            list.add("考试实际用时");
            list.add("考试时间");
            list.add("考试次数");
            list.add("客观题答对数量");
            list.add("客观题答错数量");
            list.add("主观题得分");
        }
        return list;
    }

    private EduExamDto findRecordExcelByExamId(Long examId) {
        EduExamDto examDto = new EduExamDto();
        // 考试类型
        EduExam exam = eduExamDao.getOne(examId);
        String typeName = commonTypeDao.getNameById(exam.getTypeId());
        List<EduExamTestPaper> testPaperList = examTestPaperDao.findByExamId(examId);
        examDto.setFormType(exam.getFormType());
        Double totalScore = 0.0d;
        if (!CollectionUtils.isEmpty(testPaperList)) {
            List<Long> testPaperIds = testPaperList.stream().map(r -> r.getTestPaperId()).collect(Collectors.toList());
            totalScore = testPaperDao.findAllById(testPaperIds).stream()
                    .collect(Collectors.summingDouble(EduTestPaper::getTotalScore));
        }
        // 考试人员
        List<EduRecordExcelDto> resultDto = eduExamRecordDao.findRecordExcelByExamId(examId);
        List<EduRecordExcelDto> resultDtoList = new ArrayList();
        Map<Long, List<EduRecordExcelDto>> collect = resultDto.stream().collect(Collectors.groupingBy(EduRecordExcelDto::getUserId));
        collect.forEach((k,v)->{
            EduRecordExcelDto eduRecordExcelDto = v.stream().max(Comparator.comparingLong(EduRecordExcelDto::getSubmitTimes)).orElse(null);
            resultDtoList.add(eduRecordExcelDto);
        });

        if (!CollectionUtils.isEmpty(resultDtoList)) {
            for (EduRecordExcelDto dto : resultDtoList) {
                if (dto.getIsCheck() > 0) {
                    float zgScore = 0;
                    List<EduExamUserAnswer> examRecordUserAnswer = eduExamUserAnswerDao.findByExamRecordId(dto.getId());
                    if (CollectionUtils.isEmpty(examRecordUserAnswer)) {
                        continue;
                    }
                    for (EduExamUserAnswer eduExamUserAnswer : examRecordUserAnswer) {
                        EduQuestion question = eduQuestionDao.findById(eduExamUserAnswer.getQuestionId()).get();
                        if (question.getType().equals(QuestionTypes.ZG_CHOICE.getCode())) {
                            if (eduExamUserAnswer.getScore() != null) {
                                zgScore += eduExamUserAnswer.getScore();
                            }
                        }
                    }
                    dto.setZgScore(zgScore);
                }
                if (dto.getCostTime() != null) {
                    dto.setActualTime(DateUtils.getFormatCostTime(dto.getCostTime()));
                }
            }
        }

        // 缺考人员
        List<Long> absentUsers = new ArrayList<>();
        if (exam.getFormType() == 0){
            absentUsers = getFromType(examId);
        }else {
            absentUsers = getAbsentUsers(examId);
        }
        //List<Long> absentUsers = getAbsentUsers(examId);
        //List<BaseUser> baseUserList = baseUserDao.findAllById(absentUsers);
        List<BaseUser> baseUserList = new ArrayList<>();
        if (!absentUsers.isEmpty()){
            baseUserList = baseUserMapper.selectByUserList(absentUsers);
        }
        for (BaseUser baseUser : baseUserList) {
                resultDtoList.add(new EduRecordExcelDto(baseUser.getRealName(), baseUser.getCode(), exam.getName(), null,
                        exam.getDurationMinutes(), "0", 0, 0, 0));
        }
        for (EduRecordExcelDto dto : resultDtoList) {
            dto.setExamType(typeName == null ? "" : typeName);
            dto.setTotalScore(totalScore.floatValue());
            if (exam.getFormType() == 0) {
                dto.setTotalScore(exam.getTotalScore());
            }
        }

        examDto.setResultDtoList(resultDtoList);
        return examDto;
    }

    /**
     * 计算学习时长 xxx s -> 128h30m23s
     */
    private String calLearnTime(Long courseLearnTime) {
        if (courseLearnTime == 0) {
            return courseLearnTime + "s";
        }
        long minute = courseLearnTime / 60;
        long second = courseLearnTime % 60;
        if (minute == 0) {
            return second + "s";
        }
        if (minute < 60) {
            return minute + "m" + second + "s";
        } else {
            long hour = minute / 60;
            long tempMinute = minute % 60;
            return hour + "h" + tempMinute + "m" + second + "s";
        }
    }

    private List<Long> getFromType(Long examId) {
        List<EduExamUserScope> list = eduExamUserScopeDao.findByExamId(examId);
        String objType = list.get(0).getObjType();
        List<Long> needIds;
        if ("D".equals(objType)) {
            List<Long> collect = list.stream().map(r -> r.getObjId()).collect(Collectors.toList());
            needIds = baseUserDao.findIdByDepartIdIn(collect);
        } else if ("J".equals(objType)) {
            List<Long> collect = list.stream().map(r -> r.getObjId()).collect(Collectors.toList());
            needIds = baseUserDao.findIdByJobIn(collect);
        } else {
            needIds = list.stream().map(r -> r.getObjId()).collect(Collectors.toList());
        }
        List<Long> actualIds = eduExamRecordDao.findUserIdByExamId(examId);
        needIds.removeAll(actualIds);
        return needIds;
    }

}
